function _proxy(obj, ori, subscriber, path=[]){
    return new Proxy(obj, {
        set(target, key, value, receiver) {
            let new_path
            if(target instanceof Array){
                new_path = path.concat([`[${key}]`])
            }else{
                new_path = path.concat([key])
            }
            bubble(new_path, subscriber, ori, value)
            return Reflect.set(target, key, value, receiver);
        },
        deleteProperty(target, key){
            let _path = path.concat([key])
            disSubscribeAll(_path.join('.').replace(/\.\[/g,'['))
            delete target[key];
        }
    })
}
function deepProxy(obj, ori, subscriber, path=[]){
    for(let prop in obj){
        if(typeof(obj[prop])=='object'){
            if(obj[prop] instanceof Function){
            }else{
                let path_item = (obj instanceof Array) ? `[${prop}]` : prop
                // console.log("path_item", obj[prop])
                obj[prop] = _proxy(obj[prop], ori, subscriber, path.concat([path_item]))
                deepProxy(obj[prop], ori, subscriber, path.concat([prop]))
            }
        }
    }
    return obj
}
function objByPath(obj, path){ // path: String
    path = path.replace(/\[/g, '.')
    path = path.replace(/\]/g, '')
    path  = path.split('.')
    for(let i in path){
        try{
            obj = obj[path[i]]
        }catch(e){
            console.log(e)
            return undefined
        }
    }
    return obj
}
function disSubscribeAll (path){
    delete subscriber[path]
}
// 冒泡到最顶级
function bubble(path, subscriber, data, _value){
    let key = ''
    let value
    for(let i=0; i<path.length; i++){
        if(i!=0) key += '.'
        key += `${path[i]}`
        key = key.replace(/\.\[/g, '[')
        value = objByPath(data, key)
        if(typeof(value) === 'object' && !(value instanceof Array)){
            value = {...value}
            Object.assign(value, {
                [path[path.length-1]]: _value
            })
        }else{
            value = _value
        }
        if(subscriber.hasOwnProperty(key)) {
            for(let j=0; j<subscriber[key].length; j++) {
                if(subscriber[key][j]!=null) subscriber[key][j]({path:key, value})
            }
        }
    }
}
function setByPath(path, obj, value){
    let p_path = path.split('.')
    let name = p_path.pop()
    let p_obj = objByPath(obj, p_path.join('.'))
    p_obj[name] = value
}
class Observer {
    constructor(obj) {
        this.data = obj
        this.subscriber = {}
        this.vacancy = []
        obj = _proxy(obj, obj,  this.subscriber)
        this.data = deepProxy(obj, obj, this.subscriber)
    }
}
Object.assign(Observer.prototype, {
    // 订阅
    subscribe (path, cb){
        let id = new Date().getTime()
        let index = 0
        try {
            cb.id = id
        } catch(e){
            throw new Error("call back function cann't be const")
        }
        if(this.subscriber.hasOwnProperty(path)){
            if(this.vacancy.length!==0){
                index = vacancy.shift()
                this.subscriber[path].splice(index, 1, cb)
            }else{
                index = this.subscriber[path].length
                this.subscriber[path].push(cb)
            }
        }else{
            this.subscriber[path]=[cb]
        }
        return {id, path, index, value: objByPath(this.data, path)}
    },
    // 取消订阅
    disSubscribe ({id, path, index}={}){
        if(!this.subscriber.hasOwnProperty(path)) return false
        if(this.subscriber[path][index].id == id){
            this.subscriber[path].splice(index, 1, null)
            this.vacancy.push(index)
            return true
        }else{
            return false 
        }
    },
    // 设置的新属性,仍保持观察状态
    set(path, v_obj){
        let p_path = path.split('.')
        let name = p_path.pop()
        let p_obj = objByPath(this.data, p_path.join('.'))
        p_obj[name] = _proxy(v_obj, this.subscriber, path.split('.'))
    },
})
export default Observer